#include "FIFOQueueTestSuite.h"
#include <cstdint>
#include <gtest/gtest.h>
#include <thread>
#include "Util.h"
#include "XnccType.h"
#include "xncc_random.hpp"

TestQueue* FIFOQueueTestSuite::queue = nullptr;

TEST_F(FIFOQueueTestSuite, test_queue_init)
{
#ifdef DEBUG_PROD
    xncc::tools::xncc_random<xncc::types::queue_size_t> random(24, 500);
    xncc::tools::xncc_random<std::string>               randomStr;
    for (auto i = 0; i < 1000000; i++) {
        const auto len   = random.get_value();
        const auto buf   = randomStr.get_value(int(len));
        auto       ptr_w = queue->getWriteBuffer(len);
        // std::cout << "dump write i = " << i << " len " << len << " dump:" << queue->dump() << std::endl;

        EXPECT_TRUE(ptr_w != nullptr);
        memcpy(ptr_w, buf.c_str(), len);

        queue->finishWrite();

        // auto w_len = queue->get_current_write_len();
        if (ptr_w == nullptr) {
            std::cout << "nullptr dump write i = " << i << " len " << len << " dump:" << queue->dump() << std::endl;
            return;
        }
        auto ptr = queue->getReadBuffer(len);
        // std::cout << "dump read i = " << i << " len " << len << " dump:" << queue->dump() << std::endl;
        std::string read_data(ptr, len);

        queue->finishRead();
        EXPECT_TRUE(read_data == buf);

        // auto r_len = queue->get_current_read_len();
        if (ptr == nullptr) {
            std::cout << "nullptr i = " << i << " len " << len << " dump:" << queue->dump() << std::endl;
            return;
        }
        //  EXPECT_TRUE(w_len == r_len);

        EXPECT_TRUE(ptr != nullptr);
    }
#endif
}
static constexpr uint32_t THREAD_MSG_HEAD_LEN = sizeof(xncc::msg::MsgHeaderInThread);

TEST_F(FIFOQueueTestSuite, test_queue_init_2)
{
#ifdef DEBUG_PROD

    bool ouput_debug = false;
    // static constexpr uint32_t IPC_MSG_HEAD_LEN    = sizeof(xncc::msg::MsgHeaderInThread) +
    // sizeof(xncc::types::Header);
    xncc::tools::xncc_random<xncc::types::queue_size_t> random(24, 500);
    xncc::tools::xncc_random<std::string>               randomStr;
    std::cout << "0 queue dump:" << queue->dump() << std::endl;
    for (auto i = 0; i < 1000000; i++) {
        const auto len      = random.get_value();
        const auto buf      = randomStr.get_value(int(len));
        char*      writeBuf = queue->getWriteBuffer(THREAD_MSG_HEAD_LEN + len);
        if (writeBuf) {
            xncc::msg::MsgHeaderInThread* hptr = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(writeBuf);
            hptr->len                          = static_cast<uint32_t>(len);
            hptr->id                           = 1000;
            hptr->connId                       = 1;
            hptr->tid                          = 1;
            // queue->finishWriteNotNotify();

            // char* dataBuf2 = queue->getWriteBuffer(len);
            memcpy(writeBuf + THREAD_MSG_HEAD_LEN, buf.c_str(), static_cast<uint32_t>(len));
            const auto total_len = THREAD_MSG_HEAD_LEN + len;
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 1 queue dump:" << queue->dump()
                          << std::endl;
            }

            queue->finishWrite();
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 2 queue dump:" << queue->dump()
                          << std::endl;
            }

            char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
            if (unlikely(readBuf == nullptr)) {
                std::cout << "nullptr i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
            }
            xncc::msg::MsgHeaderInThread* head = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(readBuf);
            if (head->len != len) {
                std::cout << "read error i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
            }
            EXPECT_EQ(head->len, len);
            EXPECT_EQ(head->id, 1000);
            EXPECT_EQ(head->connId, 1);
            EXPECT_EQ(head->tid, 1);

            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 3 queue dump:" << queue->dump()
                          << std::endl;
            }

            // queue->finishRead();
            char* dataBuf =
                queue->getReadBuffer(static_cast<xncc::types::queue_size_t>(head->len) + THREAD_MSG_HEAD_LEN);
            EXPECT_TRUE(dataBuf != nullptr);
            if (likely(dataBuf)) {
                // const auto data = std::string(dataBuf + THREAD_MSG_HEAD_LEN, head->len);
                // EXPECT_EQ(buf, data);
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 4 queue dump:" << queue->dump()
                              << std::endl;
                }

                queue->finishRead();
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 5 queue dump:" << queue->dump()
                              << std::endl;
                }
            }
            else {
                std::cout << "read second nullptr i = " << i << " len " << total_len << " dump:" << queue->dump()
                          << std::endl;
            }
        }
        else {
        }
    }
#endif
}

TEST_F(FIFOQueueTestSuite, test_queue_init_3)
{
#ifdef DEBUG_PROD

    for (int i = 0; i < 1000000; i++) {
        char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
        if (unlikely(readBuf != nullptr)) {
            std::cout << "error nullptr i = " << i << " len " << THREAD_MSG_HEAD_LEN << " dump:" << queue->dump()
                      << std::endl;
        }

        EXPECT_TRUE(readBuf == nullptr);
    }
#endif
}

TEST_F(FIFOQueueTestSuite, test_queue_init_4)
{
#ifdef DEBUG_PROD

    xncc::tools::xncc_random<xncc::types::queue_size_t> random(24, 1000);
    xncc::tools::xncc_random<std::string>               randomStr;
    bool                                                ouput_debug = false;
    const int                                           one_times   = 10;
    for (int i = 0; i < 1000000; i++) {
        const auto len       = random.get_value();
        const auto buf       = randomStr.get_value(int(len));
        const auto total_len = THREAD_MSG_HEAD_LEN + len;
        for (int j = 0; j < one_times; j++) {
            char* writeBuf = queue->getWriteBuffer(THREAD_MSG_HEAD_LEN + len);
            if (writeBuf) {
                xncc::msg::MsgHeaderInThread* hptr = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(writeBuf);
                hptr->len                          = static_cast<uint32_t>(len);
                hptr->id                           = 1000;
                hptr->connId                       = 1;
                hptr->tid                          = 1;
                memcpy(writeBuf + THREAD_MSG_HEAD_LEN, buf.c_str(), static_cast<uint32_t>(len));
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 1 queue dump:" << queue->dump()
                              << std::endl;
                }

                queue->finishWrite();
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 2 queue dump:" << queue->dump()
                              << std::endl;
                }
            }
        }

        for (int j = 0; j < one_times; j++) {
            char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
            if (unlikely(readBuf == nullptr)) {
                std::cout << "nullptr i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
            }
            xncc::msg::MsgHeaderInThread* head = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(readBuf);
            if (head->len != len) {
                std::cout << "read error i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
            }
            EXPECT_EQ(head->len, len);
            EXPECT_EQ(head->id, 1000);
            EXPECT_EQ(head->connId, 1);
            EXPECT_EQ(head->tid, 1);

            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 3 queue dump:" << queue->dump()
                          << std::endl;
            }

            // queue->finishRead();
            char* dataBuf =
                queue->getReadBuffer(static_cast<xncc::types::queue_size_t>(head->len) + THREAD_MSG_HEAD_LEN);
            EXPECT_TRUE(dataBuf != nullptr);
            if (likely(dataBuf)) {
                // const auto data = std::string(dataBuf + THREAD_MSG_HEAD_LEN, head->len);
                // EXPECT_EQ(buf, data);
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 4 queue dump:" << queue->dump()
                              << std::endl;
                }

                queue->finishRead();
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 5 queue dump:" << queue->dump()
                              << std::endl;
                }
            }
            else {
                std::cout << "read second nullptr i = " << i << " len " << total_len << " dump:" << queue->dump()
                          << std::endl;
            }
        }
    }
#endif
}

TEST_F(FIFOQueueTestSuite, test_queue_init_5)
{
    // #ifdef DEBUG_PROD

    xncc::tools::xncc_random<xncc::types::queue_size_t> random(24, 1000);
    xncc::tools::xncc_random<std::string>               randomStr;
    bool                                                ouput_debug = false;

    auto func = [&random, &randomStr](TestQueue* queue, bool ouput_debug) {
        for (int i = 0; i < 1000000; i++) {
            const auto len       = random.get_value();
            const auto buf       = randomStr.get_value(int(len));
            const auto total_len = THREAD_MSG_HEAD_LEN + len;
            char*      writeBuf  = queue->getWriteBuffer(THREAD_MSG_HEAD_LEN + len);
            if (writeBuf) {
                xncc::msg::MsgHeaderInThread* hptr = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(writeBuf);
                hptr->len                          = static_cast<uint32_t>(len);
                hptr->id                           = 1000;
                hptr->connId                       = 1;
                hptr->tid                          = 1;
                memcpy(writeBuf + THREAD_MSG_HEAD_LEN, buf.c_str(), static_cast<uint32_t>(len));
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 1 queue dump:" << queue->dump()
                              << std::endl;
                }

                queue->finishWrite();
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 2 queue dump:" << queue->dump()
                              << std::endl;
                }
            }
        }
    };

    std::thread t1(func, queue, ouput_debug);

    int i = 0;
    while (true) {
        char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
        if (unlikely(readBuf == nullptr)) {
            // std::cout << "nullptr i = " << i << " len " << THREAD_MSG_HEAD_LEN << " dump:" << queue->dump()
            //           << std::endl;
            if (i > 999990) {
                std::cout << i << std::endl;
            }
            continue;
        }
        xncc::msg::MsgHeaderInThread* head = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(readBuf);
        // if (head->len != len) {
        //     std::cout << "read error i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
        // }
        // EXPECT_EQ(head->len, len);
        EXPECT_EQ(head->id, 1000);
        EXPECT_EQ(head->connId, 1);
        EXPECT_EQ(head->tid, 1);

        auto total_len = head->len + THREAD_MSG_HEAD_LEN;
        ++i;
        if (ouput_debug) {
            std::cout << "index:" << i << " data_len:" << total_len << " 3 queue dump:" << queue->dump() << std::endl;
        }

        // queue->finishRead();
        char* dataBuf = queue->getReadBuffer(static_cast<xncc::types::queue_size_t>(head->len) + THREAD_MSG_HEAD_LEN);
        EXPECT_TRUE(dataBuf != nullptr);
        if (likely(dataBuf)) {
            // const auto data = std::string(dataBuf + THREAD_MSG_HEAD_LEN, head->len);
            // EXPECT_EQ(buf, data);
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 4 queue dump:" << queue->dump()
                          << std::endl;
            }

            queue->finishRead();
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 5 queue dump:" << queue->dump()
                          << std::endl;
            }
        }
        else {
            std::cout << "read second nullptr i = " << i << " len " << total_len << " dump:" << queue->dump()
                      << std::endl;
        }
        if (i % 100000 == 0) {
            std::cout << "index:" << i << " data_len:" << total_len << " 6 queue dump:" << queue->dump() << std::endl;
        }
        if (i >= 1000000) {
            break;
        }
    }

    EXPECT_TRUE(i == 1000000);
    t1.join();
    // #endif
}

TEST_F(FIFOQueueTestSuite, test_queue_read_write_file)
{
#ifdef DEBUG_PROD

    bool  ouput_debug = false;
    FILE* fp          = fopen("/dev/shm/queue_dump", "r");

    auto func = [](TestQueue* queue, bool ouput_debug, FILE* fp) {
        for (int i = 0; i < 1000000; i++) {
            int  tmpid;
            auto ret = fread(&tmpid, sizeof(tmpid), 1, fp);
            if (ret != 1) {
                std::cout << "read error i = " << i << " len " << tmpid << " dump:" << queue->dump() << std::endl;
            }
            int len;
            ret = fread(&len, sizeof(len), 1, fp);
            if (ret != 1) {
                std::cout << "read error i = " << i << " len " << tmpid << " dump:" << queue->dump() << std::endl;
            }
            const auto total_len = THREAD_MSG_HEAD_LEN + uint32_t(len);
            char*      writeBuf  = queue->getWriteBuffer(THREAD_MSG_HEAD_LEN + uint32_t(len));
            if (writeBuf) {
                xncc::msg::MsgHeaderInThread* hptr = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(writeBuf);
                hptr->len                          = static_cast<uint32_t>(len);
                hptr->id                           = 1000;
                hptr->connId                       = 1;
                hptr->tid                          = 1;
                memcpy(writeBuf + THREAD_MSG_HEAD_LEN, &i, sizeof(int));
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 1 queue dump:" << queue->dump()
                              << std::endl;
                }

                queue->finishWrite();
                if (ouput_debug) {
                    std::cout << "index:" << i << " data_len:" << total_len << " 2 queue dump:" << queue->dump()
                              << std::endl;
                }
            }
        }
        fclose(fp);
    };

    std::thread t1(func, queue, ouput_debug, fp);
    FILE*       fpr = fopen("/dev/shm/read_result", "w");

    int i = 0;
    while (true) {
        char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
        if (unlikely(readBuf == nullptr)) {
            // std::cout << "nullptr i = " << i << " len " << THREAD_MSG_HEAD_LEN << " dump:" << queue->dump()
            //           << std::endl;
            continue;
        }
        xncc::msg::MsgHeaderInThread* head = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(readBuf);
        // if (head->len != len) {
        //     std::cout << "read error i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
        // }
        // EXPECT_EQ(head->len, len);
        EXPECT_EQ(head->id, 1000);
        EXPECT_EQ(head->connId, 1);
        EXPECT_EQ(head->tid, 1);

        auto total_len = head->len + THREAD_MSG_HEAD_LEN;
        ++i;
        if (ouput_debug) {
            std::cout << "index:" << i << " data_len:" << total_len << " 3 queue dump:" << queue->dump() << std::endl;
        }

        // queue->finishRead();
        char* dataBuf = queue->getReadBuffer(static_cast<xncc::types::queue_size_t>(head->len) + THREAD_MSG_HEAD_LEN);
        EXPECT_TRUE(dataBuf != nullptr);
        if (likely(dataBuf)) {
            // const auto data = std::string(dataBuf + THREAD_MSG_HEAD_LEN, head->len);
            // EXPECT_EQ(buf, data);
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 4 queue dump:" << queue->dump()
                          << std::endl;
            }
            const int data = *(reinterpret_cast<int*>(dataBuf + THREAD_MSG_HEAD_LEN));
            fwrite(&data, sizeof(int), 1, fpr);
            const int wlen = static_cast<int>(head->len);
            fwrite(&wlen, sizeof(int), 1, fpr);
            queue->finishRead();
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 5 queue dump:" << queue->dump()
                          << std::endl;
            }
        }
        else {
            std::cout << "read second nullptr i = " << i << " len " << total_len << " dump:" << queue->dump()
                      << std::endl;
        }
        if (i % 100000 == 0) {
            std::cout << "index:" << i << " data_len:" << total_len << " 6 queue dump:" << queue->dump() << std::endl;
        }
        if (i >= 1000000) {
            break;
        }
    }

    EXPECT_TRUE(i == 1000000);
    t1.join();
#endif
}

TEST_F(FIFOQueueTestSuite, test_queue_no_expand)
{
#ifdef DEBUG_PROD

    xncc::tools::xncc_random<xncc::types::queue_size_t> random(24, 512);
    xncc::tools::xncc_random<std::string>               randomStr;
    bool                                                ouput_debug = false;

    auto func = [&random, &randomStr](TestQueue* queue, bool ouput_debug) {
        for (int i = 0; i < 1000000; i++) {
            const auto len       = random.get_value();
            const auto buf       = randomStr.get_value(int(len));
            const auto total_len = THREAD_MSG_HEAD_LEN + len;
            while (true) {
                char* writeBuf = queue->getWriteBuffer(THREAD_MSG_HEAD_LEN + len);
                if (writeBuf) {
                    xncc::msg::MsgHeaderInThread* hptr = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(writeBuf);
                    hptr->len                          = static_cast<uint32_t>(len);
                    hptr->id                           = 1000;
                    hptr->connId                       = 1;
                    hptr->tid                          = 1;
                    memcpy(writeBuf + THREAD_MSG_HEAD_LEN, buf.c_str(), static_cast<uint32_t>(len));
                    if (ouput_debug) {
                        std::cout << "index:" << i << " data_len:" << total_len << " 1 queue dump:" << queue->dump()
                                  << std::endl;
                    }

                    queue->finishWrite();
                    if (ouput_debug) {
                        std::cout << "index:" << i << " data_len:" << total_len << " 2 queue dump:" << queue->dump()
                                  << std::endl;
                    }
                    break;
                }
                else {
                    continue;
                }
            }
        }
    };

    std::thread t1(func, queue, ouput_debug);

    int i = 0;
    while (true) {
        char* readBuf = queue->getReadBuffer(THREAD_MSG_HEAD_LEN);
        if (unlikely(readBuf == nullptr)) {
            // std::cout << "nullptr i = " << i << " len " << THREAD_MSG_HEAD_LEN << " dump:" << queue->dump()
            //           << std::endl;
            if (i > 999990) {
                std::cout << i << std::endl;
            }
            continue;
        }
        xncc::msg::MsgHeaderInThread* head = reinterpret_cast<xncc::msg::MsgHeaderInThread*>(readBuf);
        // if (head->len != len) {
        //     std::cout << "read error i = " << i << " len " << total_len << " dump:" << queue->dump() << std::endl;
        // }
        // EXPECT_EQ(head->len, len);
        EXPECT_EQ(head->id, 1000);
        EXPECT_EQ(head->connId, 1);
        EXPECT_EQ(head->tid, 1);

        auto total_len = head->len + THREAD_MSG_HEAD_LEN;
        ++i;
        if (ouput_debug) {
            std::cout << "index:" << i << " data_len:" << total_len << " 3 queue dump:" << queue->dump() << std::endl;
        }

        // queue->finishRead();
        char* dataBuf = queue->getReadBuffer(static_cast<xncc::types::queue_size_t>(head->len) + THREAD_MSG_HEAD_LEN);
        EXPECT_TRUE(dataBuf != nullptr);
        if (likely(dataBuf)) {
            // const auto data = std::string(dataBuf + THREAD_MSG_HEAD_LEN, head->len);
            // EXPECT_EQ(buf, data);
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 4 queue dump:" << queue->dump()
                          << std::endl;
            }

            queue->finishRead();
            if (ouput_debug) {
                std::cout << "index:" << i << " data_len:" << total_len << " 5 queue dump:" << queue->dump()
                          << std::endl;
            }
        }
        else {
            std::cout << "read second nullptr i = " << i << " len " << total_len << " dump:" << queue->dump()
                      << std::endl;
        }
        if (i % 100000 == 0) {
            std::cout << "index:" << i << " data_len:" << total_len << " 6 queue dump:" << queue->dump() << std::endl;
        }
        if (i >= 1000000) {
            break;
        }
    }

    EXPECT_TRUE(i == 1000000);
    t1.join();
#endif
}
